#include "IInputHandler.h"
#include "ISceneManagement.h"
#include "SceneRenderer.h"

/*
*	___ SceneRenderer ___
*
*
***************************************************************/

Scene::SceneRenderer::SceneRenderer( ISceneManagement *sManager, IInputHandler *iHandler)
{
	//Inits
	_done = false;

	_displaySizeX = 1024;
	_displaySizeY = 768;
	_frame=0;
	_timebase=0;
	_frameTime = 0;

	_lastTick = 0;
	_maxFrameTime = 20;
	_timeEdge = 0;

	_fullscreen=true;

	_inputHandler = iHandler;
	_sceneManagement = sManager;
	_sceneManagement->setRenderer( this );
}

Scene::SceneRenderer::~SceneRenderer( )
{
	cout <<"~SceneRenderer( )"<<endl;
}

void Scene::SceneRenderer::HandleUserEvents(SDL_Event* event)
{
	//cout <<"SceneRenderer::HandleUserEvents(SDL_Event* event)"<<endl;
	switch (event->user.code) {
		case SceneRenderer::RUN_GAME_LOOP:
			Loop();
			break;
		case SceneRenderer::TIMER_100:
			_sceneManagement->Timer100();
			break;
	}
}


void Scene::SceneRenderer::MouseMoved( Sint16 xDelta, Sint16 yDelta )
{
	_sceneManagement->MouseMoved( xDelta,yDelta  );
	_inputHandler->MouseMoved( xDelta,yDelta  );
}

void Scene::SceneRenderer::KeyDown( Uint8 *key )
{
	_keys = key;
	_inputHandler->KeyPressed( key );
}

void Scene::SceneRenderer::KeyUp( Uint8 *key )
{
	_keys = key;
	_inputHandler->KeyReleased( key );
}

void Scene::SceneRenderer::Loop()
{
	ComputeFPS();


	SDL_GetRelativeMouseState(&_relMouseX, &_relMouseY);
	if( _relMouseX != 0|| _relMouseY != 0 )
		MouseMoved( _relMouseX, _relMouseY );

	// check all the keys for pressed. if any key is pressed it will sends to the inputListener
	//for( int i = 0;i < 256;i++ )
		//if( _keys[i] )
	_inputHandler->KeyHolding( _keys );
	//cout <<"KEY_LOOP "<<(bool)_keys[SDLK_F1]<<endl;

	// if keyboard camera moving is activated, check if there are any move key pressed!!!

	// quit the running program
	if( _keys['q'] == true )
		_done = true;


	_sceneManagement->KeyLoop(_keys);

	//-- Render the Complete Scene!!!
	Render();
}

void Scene::SceneRenderer::PreInitScene()
{
	_sceneManagement->PreInitScene();
	_sceneManagement->InitScene();
}

void Scene::SceneRenderer::Init_OpenGL()
{
	//SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);    //doublebuffering soll verwendet werden

	/* Darstellung auf gesamten Clientbereich des Fensters zulassen */
	//glViewport(0, -((GLfloat)_displaySizeX - (GLfloat)_displaySizeY)/2.0, (GLint)_displaySizeX, (GLint)_displaySizeX);

	/* Projektionsmatix initialisieren auf 60 Grad horizontales */
	/* Sichtfeld, Verhaeltnis Breite:Hoehe = 1:1, Clipping fuer z<1 */
	/* und z>200 */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	/* angle, aspect, near Clip, far Clip */
	gluPerspective(45.0, (GLfloat)_displaySizeX/(GLfloat)_displaySizeY, 0.1, 400.0);
	//gluPerspective(45.0, 1.0, 0.1, 500.0);
	//glFrustum( -5, 5, -3.125, 3.125, 1.0, 500.0 );


	/* Modelview Matrix wieder zur aktuellen Matrix machen */
	glMatrixMode(GL_MODELVIEW);

	/* Z-Buffer f. Berechnung verdeckter Flaechen einschalten */
	glEnable(GL_DEPTH_TEST);
	//weichere farbuebergaenge auf koerpern
	glShadeModel(GL_SMOOTH);

	glClearColor(0.0,0.5,1.0,1.0);
	//glClearColor( 1.0,1.0,1.0,0.0);
	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
	glEnable ( GL_COLOR_MATERIAL ) ;
	glEnable (GL_TEXTURE_2D);
	//glEnable(GL_NORMALIZE);
}


void Scene::SceneRenderer::Render()
{

	/* Modelview Matrix wieder zur aktuellen Matrix machen */
	//glMatrixMode(GL_MODELVIEW);
	//---_______________Bild initialisieren___________________---//
	/* _frameBuffer unf Z-Buffer loeschen */
  	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	/* Modelview Matrix initialisieren */
	glLoadIdentity ();

	//---_______________Primitivive Zeichnen__________________---//
	glColor3f( 1.0,1.0,1.0 );
	//cout <<"SceneRenderer::Render()1"<<endl;
	_sceneManagement->RenderObject();

	//---_________________Buffer Tauschen_____________________---//
	SDL_GL_SwapBuffers();
	//cout <<"SceneRenderer::Render()2"<<endl;
}

void Scene::SceneRenderer::Run()
{
	// Load file, etc...


	// INIT SDL Components
	SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER);

	Uint32 sdl_flags =  SDL_OPENGL  /*| SDL_DOUBLEBUF */| SDL_HWPALETTE;
	if( _fullscreen )
		sdl_flags |= SDL_FULLSCREEN;

	SDL_SetVideoMode(_displaySizeX, _displaySizeY, 16, sdl_flags);
	SDL_ShowCursor (SDL_DISABLE);
	//SDL_WM_GrabInput (SDL_GRAB_ON);
	cout <<"Scene::SceneRenderer::Run#1"<<endl;
	_timer = SDL_AddTimer(1, GameLoopTimer, 0);
	_timer2 = SDL_AddTimer(20, TimerFunction, _sceneManagement);

	// Init OpenGL
	Init_OpenGL();
	PreInitScene();

	SDL_Event event;
	event.type = SDL_USEREVENT;
	event.user.code = RUN_GAME_LOOP;
	event.user.data1 = 0;
	event.user.data2 = 0;

	SDL_PushEvent(&event);
	cout <<"set last tick"<<endl;
	_lastTick = SDL_GetTicks();// erste zeit setzen
	_keys = SDL_GetKeyState(NULL);

	cout <<"set last tick"<<endl;
	while( ( !_done ) && (SDL_WaitEvent(&ev)))//-- Get the Event State!!!
	{
		switch( ev.type )
		{
			case SDL_QUIT:
				cout <<"SDL_QUIT"<<endl;
				_done = true;
			break;

			//~ case SDL_MOUSEMOTION:
				//~ //cout <<"SDL_MOUSEMOTION"<<endl;
				//~ //_relMouseX = ev.motion.x;
				//~ SDL_GetRelativeMouseState(&_relMouseX, &_relMouseY);
				//~ //cout <<(*_relMouseX)<<endl;
				//~ MouseMoved( _relMouseX, _relMouseY );
			//~ break;
			case SDL_KEYDOWN:
				//cout <<"SDL_KEYDOWN1"<<endl;
				//_keys = SDL_GetKeyState(NULL);
				//cout <<"SDL_KEYDOWN1"<<endl;
				KeyDown( _keys );
				//cout <<"SDL_KEYDOWN2"<<endl;
			break;
			case SDL_KEYUP:
				//cout <<"SDL_KEYUP"<<endl;
				//_keys = SDL_GetKeyState(NULL);
				KeyUp( _keys );
			break;
			case SDL_USEREVENT:
				HandleUserEvents( &ev );
				//SDL_PushEvent(&event);
			break;
		}

	}
	cout <<"Run while ends1"<<endl;
	// close/remove/delete all sdl objects!!!
	SDL_RemoveTimer(_timer);
	SDL_RemoveTimer(_timer2);
	cout <<"Run while ends2"<<endl;
	SDL_Quit();
	cout <<"Run while ends3"<<endl;
}



void Scene::SceneRenderer::quit()
{
	_done = true;
}

void Scene::SceneRenderer::SetInputListener(IInputHandler *inputHandler)
{
	_inputHandler = inputHandler;
}

void Scene::SceneRenderer::SetSceneListener(ISceneManagement *sceneManager)
{
	_sceneManagement = sceneManager;
}


void Scene::SceneRenderer::ComputeFPS()
{
	//FPS messen!!!
 	_frame++;
	_mytime=SDL_GetTicks();
	if (_mytime - _timebase > 1000) {
		_sceneManagement->UpdateFPS(_frame*1000.0/(_mytime-_timebase));
		cout <<"FPS: "<<_frame*1000.0/(_mytime-_timebase)<<endl;
		_timebase = _mytime;
		_frame = 0;
	}
	_frameTime = SDL_GetTicks() - _lastTick;
	_timeEdge = _maxFrameTime - _frameTime;
	//cout <<"TIME EDGE "<<_timeEdge<<endl;
	//~ if( _timeEdge > 0 )
		//~ SDL_Delay( _timeEdge );

	_lastTick = SDL_GetTicks();
};

Sint16 Scene::SceneRenderer::getDisplayWidth() const
{
	return _displaySizeX;
}

Sint16 Scene::SceneRenderer::getDisplayHeight() const
{
	return _displaySizeY;
}


Uint32 Scene::SceneRenderer::TimerFunction( Uint32 interval, void* param )
{
	SDL_Event event;
	event.type = SDL_USEREVENT;
	event.user.code = TIMER_100;
	event.user.data1 = 0;
	event.user.data2 = 0;

	SDL_PushEvent(&event);

    return interval;
}

Uint32 Scene::SceneRenderer::GameLoopTimer(Uint32 interval, void* param)
{
	//7cout <<"game_loop_timer"<<endl;
	SDL_Event event;
	event.type = SDL_USEREVENT;
	event.user.code = RUN_GAME_LOOP;
	event.user.data1 = 0;
	event.user.data2 = 0;

	SDL_PushEvent(&event);

	return interval;
}

void Scene::SceneRenderer::enter2DMode()
{
  glMatrixMode(GL_PROJECTION);
  glPushMatrix;
  glLoadIdentity;
  gluOrtho2D(0, _displaySizeX, 0, _displaySizeY);

  glMatrixMode(GL_MODELVIEW);
  glPushMatrix;
  glLoadIdentity;

  glDisable(GL_DEPTH_TEST);
}

void Scene::SceneRenderer::leave2DMode()
{
  glMatrixMode(GL_PROJECTION);
  glPopMatrix;
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix;

  glEnable(GL_DEPTH_TEST);
}

void Scene::SceneRenderer::renderBitmapString( float x,  float y,  float z,  void *font,  string str)
{
	float height = y;
	glRasterPos3f(x, height,z);

	for (unsigned int i = 0;i < str.size();i++)
	{
		if( str.at(i) == '\n' )
		{
			height -= 0.04;
			glRasterPos3f(x, height,z);
			continue;
		}
		glutBitmapCharacter(font, str.at(i));
	}
};


/***************************************************************/
